home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Language/OS - Multiplatform Resource Library
/
LANGUAGE OS.iso
/
cpp_libs
/
hlvector.lha
/
hl_vector
/
ali.cc
next >
Wrap
C/C++ Source or Header
|
1993-08-08
|
8KB
|
277 lines
// This may look like C code, but it is really -*- C++ -*-
/*
************************************************************************
*
* Numerical Math Package
* Aitken-Lagrange interpolation
*
* The package allows one to interpolate function value for a given argument
* value using function values tabulated over either uniform or non-uniform
* grid. The latter is specified by the vector of node point abscissae.
* The uniform grid is specified by the grid mesh and the abscissa of
* the first grid point.
*
* Synopsis
* double ali(q,x0,s,y)
* double q Argument value specified
* double x0 Abscissae for the 1. grid point
* double s Grid mesh, >0
* VECTOR y Vector of function values
* tabulated at points
* x0 + s*(i-y.q_lwb()))
* The vector has to contain at
* least 2 elements
*
* double ali(q,x,y)
* const double q Argument value specified
* const VECTOR x Vector of grid node abscissae
* const VECTOR y Vector of function values
* tabulated at points x[i]
* The vector has to contain at
* least 2 elements
* Output
* Both functions return the interpolated function value at point q
* Interpolation is terminated either
* - if the difference between two successive interpolated
* values is absolutely less than EPSILON
* - if the absolute value of this difference stops
* diminishing
* - after (N-1) steps, N being the no. of elements in vector y
*
* Algorithm
* Aitken scheme of Lagrange interpolation
* Algorithm is described in the book
* "Mathematical software for computers", Institute of
* Mathematics of the Belorussian Academy of Sciences,
* Minsk, 1974, issue #4,
* p. 146 (description of ALI, DALI subroutines)
* p. 180 (description of ATSE, DATSE subroutines)
* The book essentially describes the IBM SSP package.
*
************************************************************************
*/
#include "LinAlg.h"
#include "math_num.h"
#include <std.h>
#pragma implementation
/*
*------------------------------------------------------------------------
* Class that handles the interpolation
*/
static class ALInterp
{
Vector arg(1); // [1:n] Arranged table of arguments
Vector val(1); // [1:n] Arranged table of function values
double q; // Argument value the function is to be
// interpolated at
public:
// Construct the arranged tables for the
// uniform grid
ALInterp(const double q, const double x0, const double s, const Vector& y);
// Construct the arranged tables for the
// non-uniform grid
ALInterp(const double q, const Vector& x, const Vector& y);
~ALInterp(void) {}
double interpolate(void); // Perform actual interpolation
};
/*
*------------------------------------------------------------------------
*
* Arranging data for the Aitken-Lagrange interpolation
*
* Abscissae (arg) and ordinates (val) of the grid points should be enumerated
* so that the distance abs(q-arg[i]) would increase as i increases.
* Here q is the point the function is to be interpolated at.
*
*/
// Construct the arranged tables for the
// uniform grid
ALInterp::ALInterp(const double q, const double x0,
const double s, const Vector& y)
{
const int n = y.q_no_elems();
assure( n > 1, "Vector y (function values) has to have at least 2 points");
assure( s > 0, "The grid mesh has to be positive");
arg.resize_to(n);
val.resize_to(n);
ALInterp::q = q;
// First find the index of the grid node which
// is closest to q. Assign index 1 to this
// node. Then look at neighboring grid nodes
// and assign indices to them
// (kind of breadth-first search)
int js = (int)( (q-x0)/s + 1.5 ); // Index j for the point x0+s*j
// which is the closest to q
if( js < 1 ) // Check for the case of extrapolation
js = 1; // to the left end
else if( js > n )
js = n; // or to the right end
register int dir; // Direction to the next closest
// to q grid node
dir = ( q > x0 + (js-1)*s ? 1 : -1 );
register int jcurr = js, jleft = js, jright = js;
register int i;
for(i=1; i<=n; ++i) // Pick up elements x0+s*i
{ // in the neighborhood of q
arg(i) = x0 + (jcurr-1)*s;
val(i) = y(jcurr-1+y.q_lwb()); // Once the closest to q point js
if( jright >= n ) // is found, we pick up points
dir = -1; // alternatively to the right
if( jleft <= 1 ) // and to the left of the js
dir = 1; // further and further
if( dir > 0 )
{
jcurr = ++jright;
dir = -1;
}
else
{
jcurr = --jleft;
dir = 1;
}
}
}
/*
* The function that defines the order while sorting the array of indices
* in array arg using qsort
* The function is given two pointers to elements in indices array, i.e.
* to the two indices for the _XtoQ array, _XtoQ[i] being abs(x[i]-q).
* The function returns -1, 0, or +1 depending on the fact if
* abs(x[i]-q) is less, equal, or greater than abs(x[j]-q)
*/
static Vector * _XtoQ;
int compare_indices(const short *ip, const short *jp)
{
register double delta = (*_XtoQ)(*ip) - (*_XtoQ)(*jp);
if( delta < 0 )
return -1;
else if(delta == 0)
return 0;
else
return 1;
}
// Construct the arranged tables for the
// non-uniform grid
ALInterp::ALInterp(const double q, const Vector& x, const Vector& y)
{
const int n = y.q_no_elems();
assure( n > 1, "Vector y (function values) has to have at least 2 points");
are_compatible(x,y);
arg.resize_to(n);
val.resize_to(n);
ALInterp::q = q;
// Selection is done by sorting x,y arrays
// in the way mentioned above. Fisrt an array
// of indices is created and sorted, then arg,
// val arrays are filled in using the sorted indices
short indices[n];
Vector xtoq(0,n-1);
register int i;
for(i=0; i<n; i++) // 0..n-1 correspond to the
{ // unsorted x
indices[i] = i;
xtoq(i) = fabs(q - x(i+x.q_lwb()));
}
_XtoQ = &xtoq;
// Sort indices so that
// |q-x[ind[i]]| < |q-x[ind[j]]|
// for all j>i
qsort(indices,n,sizeof(indices[0]),compare_indices);
for(i=arg.q_lwb(); i<=arg.q_upb(); i++)
{
register int ind = indices[i-arg.q_lwb()];
arg(i) = x(x.q_lwb() + ind);
val(i) = y(y.q_lwb() + ind);
}
}
/*
*------------------------------------------------------------------------
* Aitken - Lagrange process
*
* arg and val tables are assumed to be arranged in the proper way
*
*/
double ALInterp::interpolate()
{
register double valp = val(1); // The best approximation found so far
register double diffp = MAXDOUBLE; // abs(valp - prev. to valp)
register int i,j;
#ifdef DEBUG
arg.print("arg - interpolation nodes");
val.print("Arranged table of function values");
#endif
// Compute the j-th row of the Aitken scheme and
// place it in the 'val' array
for(j=2; j<=val.q_upb(); j++)
{
register double argj = arg(j);
register REAL& valj = val(j);
for(i=1; i<=j-1; i++)
valj = ( val(i)*(q-argj) - valj*(q-arg(i)) ) / (arg(i) - argj);
#ifdef DEBUG
message("\nval(j) = %g, valp = %g, arg(j) = %g",valj,valp,argj);
#endif
register double diff = fabs( valj - valp );
if( j>2 && diff == 0 ) // An exact result has been achieved
break;
if( j>4 && diff > diffp ) // Difference stops diminishing
break; // after the 4. step
valp = valj; diffp = diff;
}
return valp;
}
/*
*=======================================================================
* Root modules
*/
// Uniform mesh x[i] = x0 + s*(i-y.lwb)
double ali(const double q, const double x0, const double s, const Vector& y)
{
ALInterp al(q,x0,s,y);
return al.interpolate();
}
// Nonuniform grid with nodes in x[i]
double ali(const double q, const Vector& x, const Vector& y)
{
ALInterp al(q,x,y);
return al.interpolate();
}